#ifndef __MATRIXSTACK_H__
#define __MATRIXSTACK_H__

#include <stack>

/************************************************************************/
//#ifndef __VECTOR3_H__
//#define __VECTOR3_H__
//class Vector3
//{
//	float m_fVector[3];
//public:
//	Vector3(const float x = 0.0, const float y = 0.0, const float z = 0.0);
//	float &operator[](const int i);
//	const float &operator[](const int i) const;
//	const Vector3 operator-(const Vector3 &vector) const;
//	const Vector3 operator*(const Vector3 &vector) const;
//	void norm();
//};
//#endif
/************************************************************************/

class Vector4
{
	float m_fVector[4];
public:
	Vector4(const float val = 0.0);
	Vector4(float a, float b, float c, float d);
	float &operator[](const int i);
	const float &operator[](const int i) const;
};

/************************************************************************/

class Matrix4x4
{
	Vector4 m_fMatrix[4];
public:
	Matrix4x4(const float val = 0.0f);
	Vector4 &operator[](const int i);
	const Vector4 &operator[](const int i) const;
	const Matrix4x4 operator*(const Matrix4x4 &matrix) const;
	//void toPtr3x3(float *m3x3) const;
	void inverseRotateMatrix(float *m3x3) const;
	//friend std::ostream &operator<<(const std::ostream &, const Matrix4x4 &);
};

/************************************************************************/

class MatrixStack
{
protected:
	static const double PI;
	static const double ZERO;
	std::stack<Matrix4x4> m_matrixStack;
	mutable float m_topMatrixArray[16];
	friend std::ostream& operator<<(std::ostream &, const MatrixStack &);
public:
	MatrixStack();

	void loadMatrix(const Matrix4x4 &matrix);
	void multMatrix(const Matrix4x4 &matrix);
	void loadIdentity();

	void pushMatrix();
	void popMatrix();

	const float *top() const;
	void top(Matrix4x4 &result) const;
};

/************************************************************************/

class ModelViewMatrixStack : public MatrixStack
{
public:
	void translate(const float x, const float y, const float z);
	void rotate(const float angle, const float x, const float y, const float z);
	void scale(const float x, const float y, const float z);
	void lookAt(const float eyeX, const float eyeY, const float eyeZ,
				const float centerX, const float centerY, const float centerZ,
				const float upX, const float upY, const float upZ);
	const float *normalMatrix();
};

/************************************************************************/

class ProjectionMatrixStack : public MatrixStack
{
public:
	void frustum(const float left, const float right,
				 const float bottom, const float top,
				 const float near, const float far);
	void perspective(const float fovy, const float aspect,
					 const float near, const float far);
	void ortho(const float left, const float right,
			   const float bottom, const float top,
			   const float near, const float far);
	void ortho2D(const float left, const float right,
				 const float bottom, const float top);
};

#endif
